﻿package com.designoutput.ui.util
{

	import com.designoutput.enum.ToolType;
	import com.designoutput.event.NumberEvent;
	import com.designoutput.event.SliderEvent;
	import com.designoutput.icon.ColorMarker;
	import com.designoutput.icon.SpectrumDrag;
	import com.designoutput.manager.ResourceManager;
	import com.designoutput.ui.controls.Button;
	import com.designoutput.ui.controls.ColorPad;
	import com.designoutput.ui.controls.Input;
	import com.designoutput.ui.controls.Literal;
	import com.designoutput.ui.controls.NumberInput;
	import com.designoutput.ui.newControls.DialogBox;
	import com.designoutput.ui.newControls.MessageBox;
	import com.designoutput.ui.newControls.Slider;
	import com.designoutput.utils.Globals;
	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;


	public class ColorPicker extends DialogBox
	{
		internal var cancelButton:Button;
		internal var okButton:Button;
		internal var green:uint;
		internal var red:uint;
		internal var pad:ColorPad;
		internal var cmap:BitmapData;
		internal var hex:Input;
		internal var lmx:int=0;
		internal var lmy:int=0;
		internal var lval:NumberInput;
		internal var hval:NumberInput;
		internal var marker:Sprite;
		internal var rt:String;
		internal var drag:Sprite;
		internal var snapshot:BitmapData;
		internal var lsy:int=0;
		internal var cspectrum:BitmapData;
		internal var sval:NumberInput;
		internal var spectrum:Sprite;
		internal var saturation:Number;
		internal var brightness:Number;
		internal var bt:String;
		internal var gval:NumberInput;
		internal var map:Sprite;
		internal var ccolor:BitmapData;
		internal var blue:uint;
		internal var color:uint;
		internal var gt:String;
		internal var rval:NumberInput;
		internal var hue:Number;
		internal var bval:NumberInput;

		internal var slider:Slider;
		internal var alphaInput:NumberInput;
		internal var alphaVal:Number; // a alpha value [0,1]
		internal var showAlphaChanger:Boolean;
		
		internal var previousColor:uint;
		/**
		 * ColorPicker
		 *
		 * @param target
		 * @param pad
		 * @param showAlphaChanger: A Boolean value to indicate if show the alpha slider or not.
		 *
		 */
		public function ColorPicker(target:DesignoutputCard, pad:ColorPad, showAlphaChanger:Boolean=false)
		{
			this.SetupSnapshot(target);
			this.pad=pad;
			this.color=pad.color;
			this.previousColor = this.color;
			this.alphaVal=pad.alphaVal;
			this.showAlphaChanger=showAlphaChanger;
			this.AddMap();
			this.AddSpectrum();
			this.AddColorOutput();
			this.AddButtonsAndInputs();
			this.CalculateColors();
			this.SetMarkers(false);
			this.SetInputs();
			this.SetActiveColor();
			this.RenderMap();
		}
		
		override protected function initialize():void {
			super.initialize();
			
			setTitle(ResourceManager.getValue("rm_colorpicker"));
			
			width = 396;
			height = 237;
		}

		public function Cleanup():void
		{
			Globals.popupManager.modal.removeEventListener(MouseEvent.ROLL_OVER, this.ShowPicker, false);
			Globals.popupManager.modal.removeEventListener(MouseEvent.ROLL_OUT, this.HidePicker, false);
			Globals.popupManager.modal.removeEventListener(MouseEvent.CLICK, this.PickerClick, false);
		}

		private function AddColorOutput():void
		{
			var outer:*=new Sprite();
			outer.graphics.beginFill(9474192);
			outer.graphics.drawRect(0, 0, 50, 50);
			outer.graphics.beginFill(16777215);
			outer.graphics.drawRect(1, 1, 48, 48);
			outer.graphics.endFill();
			var bt:*=new Bitmap();
			this.ccolor=new BitmapData(46, 46, false);
			bt.bitmapData=this.ccolor;
			bt.y=2;
			bt.x=2;
			outer.addChild(bt);
			outer.x=255;
			outer.y=25;
			addChild(outer);
			this.RenderColor();
		}

		/**
		 * Called when click the "OK" button,
		 * since color had been change before this operation, so no need change color again here. 
		 * Just close the color picker.
		 * @param event
		 */
		protected function okClick(event:MouseEvent):void
		{
			this.pad.Set(this.color, this.alphaVal);
			this.Cleanup();
			close();
		}

		/**
		 * Called when click the "Cancel" button,
		 * It will restore the old color for the target element.
		 * @param event
		 * 
		 */		
		protected function cancelClick(event:MouseEvent):void
		{
			onCloseClick();
		}

		override protected function onCloseClick():void
		{
			this.pad.Set(this.previousColor, this.alphaVal);
			this.Cleanup();
			close();
		}

		private function RenderSpectrum():void
		{
			var col:uint=0;
			var nx:int=0;
			var ny:int=0;
			while (ny < 192)
			{

				col=this.GetRGB((191 - ny) / 191 * 359, 1, 1);
				nx=0;
				while (nx < 19)
				{

					this.cspectrum.setPixel(nx, ny, col);
					nx++;
				}
				ny++;
			}
		}

		private function AddMap():void
		{
			this.map=new Sprite();
			this.map.x=10;
			this.map.y=25;
			this.map.graphics.beginFill(9474192);
			this.map.graphics.drawRect(0, 0, 196, 196);
			this.map.graphics.beginFill(16777215);
			this.map.graphics.drawRect(1, 1, 194, 194);
			this.map.graphics.endFill();
			var btm:*=new Bitmap();
			this.cmap=new BitmapData(192, 192, false);
			btm.bitmapData=this.cmap;
			btm.y=2;
			btm.x=2;
			this.map.addChild(btm);
			addChild(this.map);
			this.RenderMap();
			this.marker=new Sprite();
			this.marker.addChild(new Bitmap(new ColorMarker(0, 0)));
			this.marker.x=189;
			this.marker.y=-2;
			this.map.addChild(this.marker);
			this.map.addEventListener(MouseEvent.MOUSE_DOWN, this.MapMouseDown, false, 0, true);
			this.map.addEventListener(MouseEvent.MOUSE_UP, this.MapMouseUp, false, 0, true);
		}

		private function MapMouseDown(event:MouseEvent):void
		{
			this.marker.x=this.map.mouseX - 5;
			this.marker.y=this.map.mouseY - 4;
			this.marker.startDrag(false, new Rectangle(-2, -2, 191, 191));
			this.addEventListener(Event.ENTER_FRAME, this.MapMove);
			stage.addEventListener(MouseEvent.MOUSE_UP, this.MapMouseUp, true);
		}

		private function MapMove(event:Event):void
		{
			if (this.lmx != this.marker.x || this.lmy != this.marker.y)
			{
				this.saturation=(this.marker.x + 2) / 191 * 100;
				this.brightness=100 - (this.marker.y + 2) / 191 * 100;
				this.color=this.GetRGB(this.hue, this.saturation / 100, this.brightness / 100);
				this.red=this.color >>> 16 & 255;
				this.green=this.color >>> 8 & 255;
				this.blue=this.color & 255;
				this.SetActiveColor();
				this.SetInputs();
				this.lmx=this.marker.x;
				this.lmy=this.marker.y;
			}
		}

		private function MapMouseUp(event:MouseEvent):void
		{
			this.marker.stopDrag();
			this.removeEventListener(Event.ENTER_FRAME, this.MapMove);
			stage.removeEventListener(MouseEvent.MOUSE_UP, this.MapMouseUp, true);

			this.pad.Set(this.color, this.alphaVal);
		}

		/**
		 * param1:hue 0-360
		 * param2:saturation 0-1
		 * param3:brightness/luminance 0-1
		 */
		private function GetRGB(hue:Number, saturation:Number, brightness:Number):uint
		{
			var cA:Number=NaN;
			var cR:Number=NaN;
			var cG:Number=NaN;
			var cB:Number=NaN;
			var hue1:Number=NaN;
			var hue2:int=0;
			var mission:Number=NaN;
			var temp1:Number=NaN;
			var temp2:Number=NaN;
			var temp3:Number=NaN;
			if (brightness == 0)
			{
				cR=0;
				cG=0;
				cB=0;
			}
			else if (saturation == 0)
			{
				cR=cG=cB=brightness;
			}
			else
			{
				hue1=hue / 60;
				hue2=int(hue1);
				mission=hue1 - hue2;
				temp1=brightness * (1 - saturation);
				temp2=brightness * (1 - saturation * mission);
				temp3=brightness * (1 - saturation * (1 - mission));
				switch (hue2)
				{
					case 0:
					{
						cR=brightness;
						cG=temp3;
						cB=temp1;
						break;
					}
					case 1:
					{
						cR=temp2;
						cG=brightness;
						cB=temp1;
						break;
					}
					case 2:
					{
						cR=temp1;
						cG=brightness;
						cB=temp3;
						break;
					}
					case 3:
					{
						cR=temp1;
						cG=temp2;
						cB=brightness;
						break;
					}
					case 4:
					{
						cR=temp3;
						cG=temp1;
						cB=brightness;
						break;
					}
					case 5:
					{
						cR=brightness;
						cG=temp1;
						cB=temp2;
						break;
					}
					case 6:
					{
						cR=brightness;
						cG=temp3;
						cB=temp1;
						break;
					}
					default:
					{
						break;
					}
				}
			}
			cR=cR * 255;
			cG=cG * 255;
			cB=cB * 255;
			return cR << 16 | cG << 8 | cB;
		}

		private function SetupSnapshot(parent:DesignoutputCard):void
		{
			this.snapshot=new BitmapData(parent.stage.stageWidth < 2880 ? (parent.stage.stageWidth) : (2880), parent.stage.stageHeight < 2880 ? (parent.stage.stageHeight) : (2880), false);
			this.snapshot.draw(parent);
			
			Globals.popupManager.modal.addEventListener(MouseEvent.ROLL_OVER, this.ShowPicker, false, 0, true);
			Globals.popupManager.modal.addEventListener(MouseEvent.ROLL_OUT, this.HidePicker, false, 0, true);
			Globals.popupManager.modal.addEventListener(MouseEvent.CLICK, this.PickerClick, false, 0, true);
		}

		private function HidePicker(event:MouseEvent):void
		{
			Globals.cursorManager.setCursor(ToolType.None);
		}

		private function ShowPicker(event:MouseEvent):void
		{
			Globals.cursorManager.setCursor(ToolType.Picker);
		}

		private function PickerClick(event:MouseEvent):void
		{
			this.color=this.snapshot.getPixel32(stage.mouseX, stage.mouseY);
			this.CalculateColors();
			this.SetMarkers();
			this.SetInputs();
			this.SetActiveColor();
			this.RenderMap();
		}

		/**
		 *
		 * @param showAlphaChanger: A Boolean value to indicate if the alpha changer show or not.
		 *
		 */
		private function AddButtonsAndInputs(showAlphaChanger:Boolean=false):void
		{
			cancelButton = new Button(ResourceManager.getValue("rm_cancel"), 70);
			cancelButton.x=315;
			cancelButton.y=54;
			this.cancelButton.addEventListener(MouseEvent.CLICK, this.cancelClick, false, 0, true);
			addChild(cancelButton);
			
			okButton = new Button(ResourceManager.getValue("rm_ok"), 70);
			okButton.x=315;
			okButton.y=25;
			this.okButton.addEventListener(MouseEvent.CLICK, this.okClick, false, 0, true);
			addChild(okButton);
			
			var liH:*=new Literal("H:");
			liH.x=252;
			liH.y=90;
			addChild(liH);
			var liS:*=new Literal("S:");
			liS.x=252;
			liS.y=115;
			addChild(liS);
			var liL:*=new Literal("L:");
			liL.x=252;
			liL.y=140;
			addChild(liL);
			this.hval=new NumberInput(30, 0, 0, 359);
			this.hval.x=270;
			this.hval.y=90;
			addChild(this.hval);
			this.sval=new NumberInput(30, 0, 0, 100);
			this.sval.x=270;
			this.sval.y=115;
			addChild(this.sval);
			this.lval=new NumberInput(30, 0, 0, 100);
			this.lval.x=270;
			this.lval.y=140;
			addChild(this.lval);
			var liR:*=new Literal("R:");
			liR.x=317;
			liR.y=90;
			addChild(liR);
			var liG:*=new Literal("G:");
			liG.x=317;
			liG.y=115;
			addChild(liG);
			var liB:*=new Literal("B:");
			liB.x=317;
			liB.y=140;
			addChild(liB);
			this.rval=new NumberInput(30, 0, 0, 255);
			this.rval.x=335;
			this.rval.y=90;
			addChild(this.rval);
			this.gval=new NumberInput(30, 0, 0, 255);
			this.gval.x=335;
			this.gval.y=115;
			addChild(this.gval);
			this.bval=new NumberInput(30, 0, 0, 255);
			this.bval.x=335;
			this.bval.y=140;
			addChild(this.bval);

			var li:Literal=new Literal("#");
			li.x=255;
			addChild(li);
			this.hex=new Input(50, "ffffff", 6);
			this.hex.restrict="0-9 a-f";
			addChild(this.hex);

			if (!this.showAlphaChanger)
			{
				this.hex.x=270;
				this.hex.y=180;
				li.y=180;
			}
			else
			{
				this.hex.x=270;
				this.hex.y=164;
				li.y=164;

				var liAlpha:Literal=new Literal("A:");
				liAlpha.x=252;
				liAlpha.y=190;
				addChild(liAlpha);

				this.alphaInput=new NumberInput(30, 100, 0, 100);
				this.alphaInput.x=270;
				this.alphaInput.y=190;
				this.addChild(this.alphaInput);
				this.alphaInput.addEventListener(NumberEvent.VALUE_CHANGE, this.alphaInputChange);

				this.slider=new Slider(70, 100);
				this.slider.addEventListener(SliderEvent.DRAG, this.alphaDrag, false, 0, true);
				this.slider.x=310;
				this.slider.y=190;
				this.addChild(this.slider);
			}

			this.rval.addEventListener(KeyboardEvent.KEY_UP, this.RGBInputKeyUP);
			this.gval.addEventListener(KeyboardEvent.KEY_UP, this.RGBInputKeyUP);
			this.bval.addEventListener(KeyboardEvent.KEY_UP, this.RGBInputKeyUP);
			this.hval.addEventListener(KeyboardEvent.KEY_UP, this.HSLInputKeyUP);
			this.sval.addEventListener(KeyboardEvent.KEY_UP, this.HSLInputKeyUP);
			this.lval.addEventListener(KeyboardEvent.KEY_UP, this.HSLInputKeyUP);
			this.hex.addEventListener(KeyboardEvent.KEY_UP, this.HexInputKeyUP);
		}

		private function HSLInputKeyUP(event:KeyboardEvent):void
		{
			if ((event.currentTarget as NumberInput).text != "")
			{
				this.color=this.GetRGB(int(this.hval.text), int(this.sval.text) / 100, int(this.lval.text) / 100);
				this.CalculateColors();
				this.SetActiveColor();
				this.SetMarkers();
				this.SetInputs();
				this.RenderMap();
			}
		}

		private function RGBInputKeyUP(event:KeyboardEvent):void
		{
			if ((event.currentTarget as NumberInput).text != "")
			{
				this.color = uint(this.rval.text) << 16 | uint(this.gval.text) << 8 | uint(this.bval.text);
				this.CalculateColors();
				this.SetActiveColor();
				this.SetMarkers();
				this.SetInputs();
				this.RenderMap();
			}
		}

		private function HexInputKeyUP(event:KeyboardEvent):void
		{
			this.red=0;
			this.green=0;
			this.blue=0;
			if (this.hex.length > 5)
			{
				this.blue=int("0x" + this.hex.text.substr(4, 2));
			}
			if (this.hex.length > 3)
			{
				this.green=int("0x" + this.hex.text.substr(2, 2));
			}
			if (this.hex.length > 1)
			{
				this.red=int("0x" + this.hex.text.substr(0, 2));
			}
			this.color = this.red << 16 | this.green << 8 | this.blue;
			this.CalculateColors();
			this.SetActiveColor();
			this.SetMarkers();
			this.SetInputs(true);
			this.RenderMap();
		}


		private function SetInputs(flag:Boolean=false):void
		{
			this.rval.text=this.red.toString();
			this.gval.text=this.green.toString();
			this.bval.text=this.blue.toString();
			this.hval.text=Math.round(this.hue).toString();
			this.sval.text=Math.round(this.saturation).toString();
			this.lval.text=Math.round(this.brightness).toString();
			if (!flag)
			{
				this.rt=this.red.toString(16);
				if (this.rt.length < 2)
				{
					this.rt="0" + this.rt;
				}
				this.gt=this.green.toString(16);
				if (this.gt.length < 2)
				{
					this.gt="0" + this.gt;
				}
				this.bt=this.blue.toString(16);
				if (this.bt.length < 2)
				{
					this.bt="0" + this.bt;
				}
				this.hex.text=this.rt + this.gt + this.bt;

				if (this.showAlphaChanger)
				{
					this.alphaInput.text=(this.alphaVal * 100).toString();
					this.slider.setValue(this.alphaVal);
				}
			}
		}

		private function alphaDrag(evt:SliderEvent):void
		{
			if (!evt.manual && Math.round(this.slider.getValue() * 100).toString() != this.alphaInput.text)
			{
				var percent:Number=Math.round(this.slider.getValue() * 100);
				this.alphaInput.text=percent.toString();
				this.alphaVal=percent / 100;
			}
		}

		private function alphaInputChange(evt:NumberEvent):void
		{
			var val:Number = evt.value;
			this.alphaVal=Number(val) / 100;
			this.slider.setValue(this.alphaVal);
		}

		private function RenderColor():void
		{
			this.ccolor.fillRect(new Rectangle(0, 0, 46, 46), this.color);
		}

		private function RenderMap():void
		{
			var j:int=0;
			var k:int=0;
			while (k < 192)
			{
				j=0;
				while (j < 192)
				{
					this.cmap.setPixel(j, k, this.GetRGB(this.hue, j / 191, (191 - k) / 191));
					j++;
				}
				k++;
			}
		}

		private function CalculateColors():void
		{
			this.red=this.color >>> 16 & 255;
			this.green=this.color >>> 8 & 255;
			this.blue=this.color & 255;
			var minVal:*=Math.min(this.red, this.green, this.blue);
			var maxVal:*=Math.max(this.red, this.green, this.blue);
			this.brightness=maxVal * 100 / 255;
			var result:*=maxVal - minVal;
			if (result)
			{
				this.saturation=100 * (result / maxVal);
				if (this.red == maxVal)
				{
					this.hue=(this.green - this.blue) / result * 60;
				}
				else if (this.green == maxVal)
				{
					this.hue=(2 + (this.blue - this.red) / result) * 60;
				}
				else
				{
					this.hue=(4 + (this.red - this.green) / result) * 60;
				}
				if (this.hue > 360)
				{
					this.hue=this.hue - 360;
				}
				else if (this.hue < 0)
				{
					this.hue=this.hue + 360;
				}
			}
			else
			{
				this.saturation=0;
				this.hue=0;
			}
		}

		/**
		 * Re-position the marker.
		 */
		private function SetMarkers(change:Boolean=true):void
		{
			this.drag.y=191 - Math.round(191 * (this.hue / 360)) - 1;
			this.marker.x=Math.round(191 * (this.saturation / 100)) - 2;
			this.marker.y=191 - Math.round(191 * (this.brightness / 100)) - 2;
			if(change){
				this.changeColorByMarkerPosition();
			}
		}

		private function SetActiveColor():void
		{
			this.ccolor.fillRect(new Rectangle(0, 0, 46, 23), this.color);
		}

		private function AddSpectrum():void
		{
			this.spectrum=new Sprite();
			this.spectrum.graphics.beginFill(9474192);
			this.spectrum.graphics.drawRect(0, 0, 23, 196);
			this.spectrum.graphics.beginFill(16777215);
			this.spectrum.graphics.drawRect(1, 1, 21, 194);
			this.spectrum.graphics.endFill();
			var bmp:*=new Bitmap();
			this.cspectrum=new BitmapData(19, 192, false);
			bmp.bitmapData=this.cspectrum;
			bmp.y=2;
			bmp.x=2;
			this.spectrum.addChild(bmp);
			this.spectrum.x=219;
			this.spectrum.y=25;
			addChild(this.spectrum);
			this.RenderSpectrum();
			this.drag=new Sprite();
			this.drag.addChild(new Bitmap(new SpectrumDrag(0, 0)));
			this.drag.x=-8;
			this.drag.y=-1;
			this.spectrum.addChild(this.drag);
			this.spectrum.addEventListener(MouseEvent.MOUSE_DOWN, this.SpectrumMouseDown, false, 0, true);
			this.spectrum.addEventListener(MouseEvent.MOUSE_UP, this.SpectrumMouseUp, false, 0, true);
		}

		private function SpectrumMouseDown(event:MouseEvent):void
		{
			this.drag.y=this.spectrum.mouseY - 3;
			this.drag.startDrag(false, new Rectangle(-8, -1, 0, 191));
			this.addEventListener(Event.ENTER_FRAME, this.SpectrumMove);
			stage.addEventListener(MouseEvent.MOUSE_UP, this.SpectrumMouseUp, true);
		}

		private function SpectrumMove(event:Event):void
		{
			if (this.lsy != this.drag.y)
			{
				this.hue=(191 - (this.drag.y + 1)) / 191 * 359;
				this.color=this.GetRGB(this.hue, this.saturation / 100, this.brightness / 100);
				this.red=this.color >>> 16 & 255;
				this.green=this.color >>> 8 & 255;
				this.blue=this.color & 255;
				this.SetActiveColor();
				this.RenderMap();
				this.SetInputs();
				this.lsy=this.drag.y;
			}
		}

		private function SpectrumMouseUp(event:MouseEvent):void
		{
			this.drag.stopDrag();
			this.removeEventListener(Event.ENTER_FRAME, this.SpectrumMove);
			stage.removeEventListener(MouseEvent.MOUSE_UP, this.SpectrumMouseUp, true);

			changeColorByMarkerPosition();
		}

		/**
		 * When finished click on Spectrum, change any value for HSL,RGB and Hex inputs.
		 * This method will get color by the marker's position, and change the pad color immediately.
		 */
		private function changeColorByMarkerPosition():void
		{
			this.saturation=(this.marker.x + 2) / 191 * 100;
			this.brightness=100 - (this.marker.y + 2) / 191 * 100;
			this.color=this.GetRGB(this.hue, this.saturation / 100, this.brightness / 100);
			this.pad.Set(this.color, this.alphaVal);
		}
		
		override protected function onConfirmKey():void {
			this.okClick(null);
		}
		
		override protected function onCancelKey():void {
			this.cancelClick(null);
		}
	}
}
